{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tutorial: Herramientas básicas de aprendizaje profundo privado\n",
    "\n",
    "Bienvenido al tutorial de introducción de PySyft para aprendizaje profundo, privado y descentralizado. Esta serie de archivos es una guía paso a paso para conocer las nuevas herramientas y técnicas requeridas para hacer aprendizaje profundo con modelos/datos secretos/privados sin centralizarlos bajo una autoridad.\n",
    "\n",
    "**Alcance:** Nosotros no hablaremos solamente acerca de como encriptar datos de forma decentralizada, también sobre como PySyft puede ayudar a decentralizar un ecosistema completo alrededor de datos, incluyendo las bases de datos donde estos son presentados y guardados, y los modelos neuronales que son usados para extraer información de los datos. Cuando nuevas extensiones de PySyft sean creadas, estos archivos serán extendidos con nuevos tutoriales para explicar las nuevas funciones.\n",
    "\n",
    "Autores:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "\n",

    "Traductores:\n",
    "- Arturo Márquez Flores - Twitter: [@arturomf94](https://twitter.com/arturomf94)\n",
    "- Ricardo Pretelt - Twitter: [@ricardopretelt](https://twitter.com/ricardopretelt)\n",
    "\n",

    "## Descripción general:\n",
    "\n",
    "- Parte 1: Herramientas básicas de aprendizaje profundo privado \n",
    "\n",
    "\n",
    "## ¿Por qué hacer este tutorial?\n",
    "\n",
    "**1) Una ventaja competitiva en tu carrera** - En los últimos 20 años, la revolución digital ha hecho que los datos sean más y más accesibles en grandes cantidades mientras que los procesos análogos se convierten en digitales. Sin embargo, con nuevas regulaciones como [GDPR](https://eugdpr.org/), las empresas están bajo presión para tener menos libertad sobre como usan y analizan información personal. **Para concluir:** Los científicos de datos no van a tener mucho acceso a datos de la misma forma que antes, pero aprendiendo las herramientas de aprendizaje profundo privado, puedes estar arriba de esta curva y ganar una ventaja competitiva en tu carrera.\n",
    "\n",
    "**2) Oportunidades empresariales** - Hay muchos problemas en la sociedad que pueden ser resueltos con aprendizaje profundo pero muchos de estos no han sido explorados porque requieren acceso a información increíblemente sensible para las personas. (Considera por ejemplo usar aprendizaje profundo para ayudar a personas con problemas mentales o de relaciones). Por lo tanto, el aprendizaje profundo privado abre muchas oportunidades de emprendimiento para tí que antes no estaban disponibles a otros sin estas herramientas.\n",
    "\n",
    "**3) Bien social** - El aprendizaje profundo puede ser usado para resolver una amplia variedad de problemas en el mundo real, pero aprendizaje profundo con *información personal* es aprendizaje profundo sobre personas, *para personas*. \n",
    "Aprender como hacer aprendizaje profundo con datos que no te pertencen representan más que una oportunidad de emprendimiento para tu carrera, es la oportunidad de ayudar a resolver uno de los problemas más importantes y personales en la vida de las personas - y hacerlo a escala.\n",
    "\n",
    "## ¿Cómo consigo créditos extra?\n",
    "\n",
    "- Dale una estrella a PySyft en GitHub! - [https://github.com/OpenMined/PySyft](https://github.com/OpenMined/PySyft)\n",
    "- Haz un video en Youtube enseñando este tutorial!\n",
    "\n",
    "\n",
    "... ok ... ¡Hagámoslo!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parte -1: Prerequisitos\n",
    "\n",
    "- Conocer PyTorch - Si no, entonces toma el curso de  http://fast.ai y regresa después\n",
    "- Lee el paper de la plataforma de PySyft https://arxiv.org/pdf/1811.04017.pdf! Esto te dará un conocimiento completo sobre como PySyft está construido y eso hará que las cosas tengan más sentido."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parte 0: Preparación\n",
    "\n",
    "Para empezar, necesitarás estar seguro de que tienes lo necesario correctamente instalado. Para esto, dirígete al readme  de PySyft y sigue las intruscciones de instalación. Para algunos, muy largo para leer.\n",
    "\n",
    "- Instalar Python 3.5 o en adelante\n",
    "- Instalar PyTorch 1.1\n",
    "- Clona PySyft (git clone https://github.com/OpenMined/PySyft.git)\n",
    "- cd PySyft\n",
    "- pip install -r pip-dep/requirements.txt\n",
    "- pip install -r pip-dep/requirements_udacity.txt\n",
    "- python setup.py install udacity\n",
    "- python setup.py test\n",
    "\n",
    "Si alguna parte de esto no funciona (o alguna de las pruebas falla) - primero chequea el [README](https://github.com/OpenMined/PySyft.git) para ayuda de instalación o abre un Issue de Github o escribe en el canal #beginner en ¡nuestro slack! [slack.openmined.org](http://slack.openmined.org/)"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "# Corre esta celda para ver si todo funciona\n",
    "import sys\n",
    "\n",
    "import torch\n",
    "from torch.nn import Parameter\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "import syft as sy\n",
    "hook = sy.TorchHook(torch)\n",
    "\n",
    "torch.tensor([1,2,3,4,5])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si esta celda es ejecutada exitósamente, entonces ¡Estás listo para comenzar! ¡Hagámoslo!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parte 1: Las herramientas básicas de ciencia de datos privada y descentralizada.\n",
    "\n",
    "Entonces - la primera pregunta que te debes estar haciendo es - ¿Cómo es posible entrenar un modelo con datos a los que no tenemos acceso? \n",
    "\n",
    "Bien, la respuesta es sorprendentemente simple. Si estas acostumbrado a trabajar con Pytorch, entonces estás acostumbrado a trabajar con objetos torch.Tensor como estos!"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "x = torch.tensor([1,2,3,4,5])\n",
    "y = x + x\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Obviamente, usar estos elegantes (y poderosos!) tensores es importante, pero también requiere que tengas los datos en tu máquina local. Aquí es donde nuestro viaje comienza.\n",
    "\n",
    "# Sección 1.1 - Enviar tensores a la máquina de Bob.\n",
    "\n",
    "Mientras normalmente haríamos ciencia de datos / aprendizaje profundo en la máquina que tiene los datos, ahora queremos realizar esta computación en **otra** máquina. Más específicamente, nosotros ya no podemos asumir que los datos estarán en nuestra máquina local. \n",
    "\n",
    "Por lo tanto, en vez de usar tensores de Torch, vamos a trabajar con **punteros** a los tensores. Déjame mostrarte a lo que me refiero. Primero crearemos una \"aparente\" máquina, que pertenece a una \"aparente\" persona. La llamaremos Bob. "
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,

   "metadata": {},
   "outputs": [],
   "source": [
    "bob = sy.VirtualWorker(hook, id=\"bob\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Digamos que la máquina de Bob está en otro planeta - ¡quizás en Marte! Pero, en el momento la máquina está vacía. Vamos a crear algunos datos para enviarlos a Bob y aprender sobre punteros!"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,

   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5])\n",
    "y = torch.tensor([1,1,1,1,1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y ahora - Enviemos nuestros tensores a Bob!!"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,

   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr = x.send(bob)\n",
    "y_ptr = y.send(bob)"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "BOOM! Ahora Bob tiene ¡dos tensores! ¿No me crees? ¡Mira por tí mismo!"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,

   "metadata": {},
   "outputs": [],
   "source": [
    "z = x_ptr + x_ptr"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "z"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [

    "Ahora mira algo. Cuando llamamos `x.send(bob)` se devolvió un nuevo objeto llamado `x_ptr`. Este es nuestro primer *puntero* a un tensor. Los punteros a tensores en realidad no tienen ningún dato por sí mismos. Sólo contienen metadatos sobre los tensores (con datos) guardados en otra máquina. El propósito de estos tensores es dar una API intuitiva para decirle a la otra máquina que compute funciones usando este tensor. Miremos la metadata que contienen estos punteros."
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mira la metadata!\n",
    "\n",
    "Hay dos atributos principales específicamente para punteros:\n",
    "\n",
    "- `x_ptr.location : bob`, la ubicación, una referencia al lugar que el puntero está apuntando.\n",
    "- `x_ptr.id_at_location : <random integer>`, el id donde el tensor está guardado en la ubicación.\n",
    "\n",
    "Están impresas en el formato `<id_at_location>@<location>`\n",
    "\n",
    "Hay también otros atributos más genéricos:\n",
    "- `x_ptr.id : <random integer>`, el id de nuestro tensor puntero, fue ubicado aleatoriamente.\n",
    "- `x_ptr.owner : \"me\"`, el trabajador al que le pertenece el tensor puntero, aquí es el trabajador local, llamado \"me\""
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "x_ptr.location"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "bob"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "bob == x_ptr.location"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "x_ptr.id_at_location"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "x_ptr.owner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tú te puedes preguntar ¿Por qué el trabajador local, el cuál también  tiene un puntero, es también un VirtualWorker aunque nosotros no lo creamos?. De hecho, así como tenemos un objeto VirtualWorker para Bob, (por predeterminado) siempre tendremos uno para nosotros también. Este trabajador es automáticamente creado cuando llamamos `hook = sy.TorchHook()` entonces tú no siempre tendrás que crearlo por tí mismo."

   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "me = sy.local_worker\n",
    "me"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "me == x_ptr.owner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y finalmente, así como podemos llamar .send() en un tensor, podemos llamar .get() en un puntero a un tensor para tenerlo de vuelta."
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "x_ptr.get()"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "y_ptr"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "y_ptr.get()"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y así como puedes ver... ¡Bob ya no más tiene los tensores! ¡Se han movido devuelta a nuestra máquina!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Sección 1.2 - Usar punteros de tensores\n",
    "\n",
    "Entonces, enviar y recibir tensores de Bob es genial, pero ¡difícilmente esto es deep learning! Queremos poder hacer _operaciones_ de tensores en tensores remotos. Afortunadamente los punteros de tensores ¡lo hacen muy fácil! Puedes usar punteros ¡así como normalmente usas tensores!"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5]).send(bob)\n",
    "y = torch.tensor([1,1,1,1,1]).send(bob)"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,

   "metadata": {},
   "outputs": [],
   "source": [
    "z = x + y"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [

    "Y voilà! \n",
    "\n",
    "Detrás de escenas, algo muy poderoso sucedió. En vez de que _x_ y _y_ se sumaran localmente, un comando fue serializado y enviado a Bob, quién realizó la operación. creó un tensor z, y luego regresó el puntero a z ¡de vuelta a nosotros!\n",

    "\n",
    "Si llamamos .get() en el puntero, ¡recibiremos el resultado de vuelta a nuestra máquina!"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Funciones de Torch\n",
    "\n",
    "Esta API ha sido extendida a ¡todas las operaciones de Torch!"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "y"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "z = torch.add(x,y)\n",
    "z"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Variables (Incluyendo propagación hacia atrás)"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,

   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5.], requires_grad=True).send(bob)\n",
    "y = torch.tensor([1,1,1,1,1.], requires_grad=True).send(bob)"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,

   "metadata": {},
   "outputs": [],
   "source": [
    "z = (x + y).sum()"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "z.backward()"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,

   "metadata": {},
   "outputs": [],
   "source": [
    "x = x.get()"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",

   "execution_count": null,
   "metadata": {},
   "outputs": [],

   "source": [
    "x.grad"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [

    "Así como puedes ver, la API es muy flexible y capaz de realizar casi todas las operaciones que normalmente harías en Torch con datos remotos. Esto pone las bases para protocolos más avanzados para preservar privacidad como aprendizaje federado,\n",
    "\n",
    "computación segura multipartita, y ¡privacidad diferencial!"

   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# !Felicitaciones! - !Es hora de unirte a la comunidad!\n",
    "\n",
    "¡Felicitaciones por completar esta parte del tutorial! Si te gustó y quieres unirte al movimiento para preservar la privacidad, propiedad descentralizada de IA y la cadena de suministro de IA (datos), puedes hacerlo de las ¡siguientes formas!\n",
    "\n",
    "### Dale una estrella a PySyft en GitHub\n",
    "\n",
    "La forma más fácil de ayudar a nuestra comunidad es por darle estrellas a ¡los repositorios de Github! Esto ayuda a crear consciencia de las interesantes herramientas que estamos construyendo.\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### ¡Únete a nuestro Slack!\n",
    "\n",
    "La mejor manera de mantenerte actualizado con los últimos avances es ¡unirte a la comunidad! Tú lo puedes hacer llenando el formulario en [http://slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### ¡Únete a un proyecto de código!\n",
    "\n",
    "La mejor manera de contribuir a nuestra comunidad es convertirte en un ¡contribuidor de código! En cualquier momento puedes ir al _Github Issues_ de PySyft y filtrar por \"Proyectos\". Esto mostrará todos los tiquetes de nivel superior dando un resumen de los proyectos a los que ¡te puedes unir! Si no te quieres unir a un proyecto, pero quieres hacer un poco de código, también puedes mirar más mini-proyectos \"de una persona\" buscando por Github Issues con la etiqueta \"good first issue\".\n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### Donar\n",
    "\n",
    "Si no tienes tiempo para contribuir a nuestra base de código, pero quieres ofrecer tu ayuda, también puedes aportar a nuestro *Open Collective\"*. Todas las donaciones van a nuestro *web hosting* y otros gastos de nuestra comunidad como ¡hackathons y meetups!\n",
    "\n",
    "[OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
